package com.cloud.sample.provider;

import com.cloud.common.sentinel.Sentinel;
import com.cloud.common.util.JsonUtils;
import com.cloud.sample.provider.dto.QcloudCosCredentialDTO;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.DeleteObjectsRequest;
import com.qcloud.cos.region.Region;
import com.tencent.cloud.CosStsClient;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.time.Duration;
import java.util.List;
import java.util.TreeMap;

@Sentinel
@Component
public class QcloudCosProvider {
    @Value("${cos.secretId}")
    private String secretId;

    @Value("${cos.secretKey}")
    private String secretKey;

    @Value("${cos.bucket}")
    private String bucket;

    @Value("${cos.region}")
    private String region;

    @Value("${cos.url}")
    private String url;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private long durationSeconds = 1800;
    private COSClient cosClient;

    @PostConstruct
    public void init() {
        COSCredentials credentials = new BasicCOSCredentials(secretId, secretKey);
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        clientConfig.setHttpProtocol(HttpProtocol.https);
        cosClient = new COSClient(credentials, clientConfig);
    }

    public void putObject(String fileName, InputStream input) {
        cosClient.putObject(bucket, fileName, input, null);
    }

    public void deleteObject(String fileName) {
        cosClient.deleteObject(bucket, fileName);
    }

    public void deleteObjects(List<String> fileNameList) {
        if (CollectionUtils.isEmpty(fileNameList)) {
            return;
        }
        String[] fileNames = new String[fileNameList.size()];
        DeleteObjectsRequest request = new DeleteObjectsRequest(bucket).withKeys(fileNameList.toArray(fileNames));
        cosClient.deleteObjects(request);
    }

    public QcloudCosCredentialDTO getCredential() throws Exception {
        final String key = QcloudCosCredentialDTO.class.getSimpleName();
        QcloudCosCredentialDTO cosCredentialDTO = (QcloudCosCredentialDTO) redisTemplate.opsForValue().get(key);
        if (cosCredentialDTO != null) {
            return cosCredentialDTO;
        }
        JSONObject credential = CosStsClient.getCredential(buildConfig());
        cosCredentialDTO = JsonUtils.parseObject(credential.toString(), QcloudCosCredentialDTO.class);
        cosCredentialDTO.setBucket(bucket);
        cosCredentialDTO.setRegion(region);
        cosCredentialDTO.setUrl(url);
        cosCredentialDTO.getCredentials().setDurationSeconds(durationSeconds);
        redisTemplate.opsForValue().set(key, cosCredentialDTO, Duration.ofSeconds(durationSeconds - 300));
        return cosCredentialDTO;
    }

    private TreeMap<String, Object> buildConfig() {
        TreeMap<String, Object> config = new TreeMap<>();
        config.put("SecretId", secretId);
        config.put("SecretKey", secretKey);
        // 临时密钥有效时长，单位是秒，默认1800秒，目前主账号最长2小时（即7200秒），子账号最长36小时（即129600秒）
        config.put("durationSeconds", durationSeconds);
        config.put("bucket", bucket);
        config.put("region", region);
        config.put("allowPrefix", "*");
        // 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
        String[] allowActions = new String[]{
                // 简单上传
                "name/cos:PutObject",
                // 表单上传、小程序上传
                "name/cos:PostObject",
                // 分片上传
                "name/cos:InitiateMultipartUpload",
                "name/cos:ListMultipartUploads",
                "name/cos:ListParts",
                "name/cos:UploadPart",
                "name/cos:CompleteMultipartUpload"
        };
        config.put("allowActions", allowActions);
        return config;
    }
}
